gdk: Add gdk_window_set_pass_through
authorAlexander Larsson <alexl@redhat.com>
Mon, 8 Jun 2015 13:36:43 +0000 (15:36 +0200)
committerAlexander Larsson <alexl@redhat.com>
Mon, 15 Jun 2015 08:56:45 +0000 (10:56 +0200)
An pass_through window is something you can draw in but does not
affect event handling. Normally if a window has with no event mask set
for a particular event then input events in it go to its parent window
(X11 semantics), whereas if pass_through is enabled the window below
the window will get the event. The later mode is useful when the
window is partially transparent. Note that an pass-through windows can
have child windows that are not pass-through so they can still get events
on some parts.

Semantically, this behaves the same as an regular window with
gdk_window_set_child_input_shapes() called on it (and re-called any
time a child is changed), but its far more efficient and easy to use.

This allows us to fix the testoverlay input stacking test.

https://bugzilla.gnome.org/show_bug.cgi?id=750568

https://bugs.freedesktop.org/show_bug.cgi?id=90917

gdk/gdkinternals.h
gdk/gdkwindow.c
gdk/gdkwindow.h

index d1d1e4d1676c062458526e54d8368220756407dd..e5ec4b204d5790fb911e34c4b488f37f50dd1b45 100644 (file)
@@ -314,6 +314,7 @@ struct _GdkWindow
   guint8 fullscreen_mode;
 
   guint input_only : 1;
+  guint pass_through : 1;
   guint modal_hint : 1;
   guint composited : 1;
   guint has_alpha_background : 1;
index 612d796a2f6eb96f361138ead58d11e1c4261758..faa848c243b85276e1e4ae63e5bb40714a3dbf7d 100644 (file)
@@ -6784,6 +6784,64 @@ gdk_window_set_child_input_shapes (GdkWindow *window)
   do_child_input_shapes (window, FALSE);
 }
 
+/**
+ * gdk_window_set_pass_through:
+ * @window: a #GdkWindow
+ * @pass_through: a boolean
+ *
+ * Sets whether input to the window is passed through to the window
+ * below.
+ *
+ * The default value of this is %FALSE, which means that pointer
+ * events that happen inside the window are send first to the window,
+ * but if the event is not selected by the event mask then the event
+ * is sent to the parent window, and so on up the hierarchy.
+ *
+ * If @pass_through is %TRUE then such pointer events happen as if the
+ * window wasn't there at all, and thus will be sent first to any
+ * windows below @window. This is useful if the window is used in a
+ * transparent fashion. In the terminology of the web this would be called
+ * "pointer-events: none".
+ *
+ * Note that a window with @pass_through %TRUE can still have a subwindow
+ * without pass through, so you can get events on a subset of a window. And in
+ * that cases you would get the in-between related events such as the pointer
+ * enter/leave events on its way to the destination window.
+ *
+ * Since: 3.18
+ **/
+void
+gdk_window_set_pass_through (GdkWindow *window,
+                            gboolean pass_through)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  window->pass_through = !!pass_through;
+
+  /* Pointer may have e.g. moved outside window due to the input region change */
+  _gdk_synthesize_crossing_events_for_geometry_change (window);
+}
+
+/**
+ * gdk_window_get_pass_through:
+ * @window: a #GdkWindow
+ * @pass_through: a boolean
+ *
+ * Returns whether input to the window is passed through to the window
+ * below.
+ *
+ * See gdk_window_set_pass_through() for details
+ *
+ * Since: 3.18
+ **/
+gboolean
+gdk_window_get_pass_through (GdkWindow *window)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  return window->pass_through;
+}
+
 /**
  * gdk_window_merge_child_input_shapes:
  * @window: a #GdkWindow
@@ -7133,6 +7191,63 @@ point_in_window (GdkWindow *window,
                          x, y));
 }
 
+/* Same as point_in_window, except it also takes pass_through and its
+   interaction with child windows into account */
+static gboolean
+point_in_input_window (GdkWindow *window,
+                      gdouble    x,
+                      gdouble    y,
+                      GdkWindow **input_window,
+                      gdouble   *input_window_x,
+                      gdouble   *input_window_y)
+{
+  GdkWindow *sub;
+  double child_x, child_y;
+  GList *l;
+
+  if (!point_in_window (window, x, y))
+    return FALSE;
+
+  if (!window->pass_through)
+    {
+      if (input_window)
+       {
+         *input_window = window;
+         *input_window_x = x;
+         *input_window_y = y;
+       }
+      return TRUE;
+    }
+
+  /* For pass-through, must be over a child input window */
+
+  /* Children is ordered in reverse stack order, i.e. first is topmost */
+  for (l = window->children; l != NULL; l = l->next)
+    {
+      sub = l->data;
+
+      if (!GDK_WINDOW_IS_MAPPED (sub))
+       continue;
+
+      gdk_window_coords_from_parent ((GdkWindow *)sub,
+                                    x, y,
+                                    &child_x, &child_y);
+      if (point_in_input_window (sub, child_x, child_y,
+                                input_window, input_window_x, input_window_y))
+       {
+         if (input_window)
+           gdk_window_coords_to_parent (sub,
+                                        *input_window_x,
+                                        *input_window_y,
+                                        input_window_x,
+                                        input_window_y);
+         return TRUE;
+       }
+    }
+
+  return FALSE;
+}
+
 static GdkWindow *
 convert_native_coords_to_toplevel (GdkWindow *window,
                                   gdouble    child_x,
@@ -7226,7 +7341,8 @@ _gdk_window_find_child_at (GdkWindow *window,
          gdk_window_coords_from_parent ((GdkWindow *)sub,
                                          x, y,
                                          &child_x, &child_y);
-         if (point_in_window (sub, child_x, child_y))
+         if (point_in_input_window (sub, child_x, child_y,
+                                    NULL, NULL, NULL))
            return (GdkWindow *)sub;
        }
 
@@ -7249,7 +7365,7 @@ _gdk_window_find_descendant_at (GdkWindow *window,
                                gdouble   *found_x,
                                gdouble   *found_y)
 {
-  GdkWindow *sub;
+  GdkWindow *sub, *input_window;
   gdouble child_x, child_y;
   GList *l;
   gboolean found;
@@ -7270,11 +7386,12 @@ _gdk_window_find_descendant_at (GdkWindow *window,
              gdk_window_coords_from_parent ((GdkWindow *)sub,
                                              x, y,
                                              &child_x, &child_y);
-             if (point_in_window (sub, child_x, child_y))
+             if (point_in_input_window (sub, child_x, child_y,
+                                        &input_window, &child_x, &child_y))
                {
                  x = child_x;
                  y = child_y;
-                 window = sub;
+                 window = input_window;
                  found = TRUE;
                  break;
                }
index ad5e88cc14cb61c54de4cad038253d2f41f8ab2c..b42f9dfe935cc1cb1ce367c0ed03507e269339d6 100644 (file)
@@ -654,6 +654,12 @@ GDK_AVAILABLE_IN_ALL
 void gdk_window_merge_child_input_shapes   (GdkWindow       *window);
 
 
+GDK_AVAILABLE_IN_3_18
+void gdk_window_set_pass_through (GdkWindow *window,
+                                 gboolean pass_through);
+GDK_AVAILABLE_IN_3_18
+gboolean gdk_window_get_pass_through (GdkWindow *window);
+
 /*
  * Check if a window has been shown, and whether all its
  * parents up to a toplevel have been shown, respectively.